package com.lagou.edu.support;

import com.lagou.edu.connector.Endpoint;
import com.lagou.edu.container.Context;
import com.lagou.edu.container.Engine;
import com.lagou.edu.container.Host;
import com.lagou.edu.container.WebXmlResolver;
import com.lagou.edu.utils.ConnectorFactory;
import com.lagou.edu.utils.FileUtil;
import com.lagou.edu.utils.MiniAppClassLoader;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author liangzj
 * @date 2021/4/10 0:46
 */
public class ServerXmlResolver {

    private Server server;

    public ServerXmlResolver(Server server) {
        this.server = server;
    }

    public void resolver() throws DocumentException {
        // 读取配置文件
        InputStream serverXmlInputStream = Server.class.getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        Document serverXmlDoc = saxReader.read(serverXmlInputStream);
        // <Server>
        Element serverElement = serverXmlDoc.getRootElement();
        // List<Service>
        List<Element> serviceElementList = serverElement.selectNodes("Service");

        for (Element serviceElement : serviceElementList) {
            // 解析连接器
            List<Element> connectorElementList = serviceElement.selectNodes("Connector");
            List<Endpoint> endpoints = resolveConnectors(connectorElementList);

            // 解析应用上下文
            Element engineElement = (Element) serviceElement.selectSingleNode("Engine");
            Engine engine = resolveEngine(engineElement);

            // 绑定连接器和engine
            server.addService(new Service(endpoints, engine));
        }
    }

    /**
     * 解析连接器配置信息
     *
     * @param connectorElementList
     * @return
     */
    private List<Endpoint> resolveConnectors(List<Element> connectorElementList) {

        return connectorElementList.stream().map(connectorElement -> {
            String port = connectorElement.attributeValue("port");
            String protocol = connectorElement.attributeValue("protocol");

            // 根据协议获取连接器对象
            Endpoint endpoint = ConnectorFactory.getConnector(protocol);
            endpoint.setPort(Integer.valueOf(port));
            endpoint.setProtocol(protocol);

            return endpoint;
        }).collect(Collectors.toList());

    }

    private Engine resolveEngine(Element engineElement) {
        Engine engine = new Engine();

        List<Element> hostElements = engineElement.selectNodes("Host");
        List<Host> hosts = resolveHost(hostElements);

        engine.setHosts(hosts);
        return engine;
    }

    private List<Host> resolveHost(List<Element> hostElements) {
        return hostElements.stream().map(hostElement -> {
            String name = hostElement.attributeValue("name");
            String appBase = hostElement.attributeValue("appBase");

            List<Context> contexts = resolveContexts(appBase);

            Host host = new Host(name, appBase, contexts);
            return host;
        }).collect(Collectors.toList());
    }

    /**
     * 解析应用配置信息
     *
     * @param appBase
     * @return
     */
    private List<Context> resolveContexts(String appBase) {

        File webappsFile = new File(appBase);

        // 路径下的所有文件夹都当作一个应用的根目录
        return Arrays.stream(webappsFile.listFiles())
                .map(webFile -> this.resolveContext(webFile))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    /**
     * 解析应用的web.xml文件
     *
     * @param webFile
     * @return
     */
    private Context resolveContext(File webFile) {
        Context context = new Context();
        context.setName("/" + webFile.getName());

        File[] files = FileUtil.searchFileByName(webFile, "web.xml");
        if (files == null || files.length == 0) {
            return null;
        }

        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(files[0]);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        MiniAppClassLoader miniAppClassLoader = getMiniClassLoader(webFile.getAbsolutePath());
        WebXmlResolver webXmlResolver = new WebXmlResolver(miniAppClassLoader, context);
        webXmlResolver.loadServlets(fileInputStream);

        return context;
    }

    private MiniAppClassLoader getMiniClassLoader(String absolutePath) {
        return new MiniAppClassLoader(absolutePath);
    }

}
